package kino.client;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import kino.command.CommandInterpreter;
import kino.command.KinoCommand;
import kino.database.KinoDB;

import java.io.IOException;
import java.util.Map;

public class KinoClient {

    /**
     * 当前客户端连接的数据库 通过select命令切换数据库
     */
    private KinoDB db;

    /**
     * aof客户端标志，该标志意味着该客户端是为了执行aof文件创建出来的伪客户端
     */
    private boolean aofClient = false;

    /**
     * 没解码前的请求
     */
    private byte[] querybuf;
    /**
     * 解码后的命令 如 set name zxz
     */
    private StringBuffer queryBuffer;
    /**
     * 编码后的 命令返回值
     */
    private StringBuffer resultBuffer;
    /**
     * 基于Netty创建的客户端连接
     */
    private Channel channel;

    /**
     * 命令表 当前客户端保存一份引用，实际上全局所有的客户端都是一份命令表
     * 有一个想法 每个客户端可以加载自己的命令表，不同类型的客户端可以有自己的定制命令表
     */
    private CommandInterpreter commandInterpreter;

    /**
     * 客户端命令被执行时 绑定命令
     */
    private KinoCommand kinoCommand;

    /**
     * 客户端命令被执行时 根据命令解析暂存解析后的扩展参数
     */
    private Map<String, Object> extParam;

    /**
     * 客户端名称
     */
    private String name;

    /**
     * 客户端id
     */
    private String id;

    /**
     * 客户端解析命令时的状态
     */
    private State state;
    public State getState() {
        return state;
    }
    public void checkpoint(State state) {
        this.state = state;
    }

    // netty
    public KinoClient(KinoDB kinoDB, Channel channel,CommandInterpreter commandInterpreter) {
        this.db = kinoDB;
        this.channel = channel;
        this.resultBuffer = new StringBuffer();
        this.queryBuffer = new StringBuffer();
        this.commandInterpreter = commandInterpreter;
        this.state = State.NUMBER_OF_ARGS;
    }

    public KinoDB getDb() {
        return db;
    }

    public void setDb(KinoDB db) {
        this.db = db;
    }

    public StringBuffer getResultBuffer() {
        return resultBuffer;
    }

    public void setResultBuffer(StringBuffer stringBuffer) {
        this.resultBuffer = stringBuffer;
    }

    public void resultAppend(String str) {
        resultBuffer.append(str);
    }

    public StringBuffer getQueryBuffer() {
        return queryBuffer;
    }

    public void setQueryBuffer(StringBuffer queryBuffer) {
        this.queryBuffer = queryBuffer;
    }

    public void queryBufferAppend(String str) {
        this.queryBuffer.append(str);
    }
    public void execute() {
        commandInterpreter.execute(this, this.queryBuffer.toString());
        this.queryBuffer.setLength(0);
        // netty模式
        if (this.channel != null) {
            try {
                this.write();
            } catch (Exception e) {

            }
        }
    }

    public void write() throws IOException {
        //包裹为字节数组
        if (channel != null) {
            ByteBuf buffer = channel.alloc().buffer();
            buffer.writeBytes(resultBuffer.toString().getBytes());
            channel.writeAndFlush(buffer);
        }
        resultBuffer.setLength(0);
    }

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }

    public boolean isAofClient() {
        return aofClient;
    }

    public void setAofClient(boolean aofClient) {
        this.aofClient = aofClient;
    }

    public byte[] getQuerybuf() {
        return querybuf;
    }

    public void setQuerybuf(byte[] querybuf) {
        this.querybuf = querybuf;
    }

    public void bindCommand(KinoCommand command) {
        this.kinoCommand = command;
    }
    public void unBindCommand() {
        this.kinoCommand = null;
        this.extParam = null;
    }
    public KinoCommand getKinoCommand() {
        return kinoCommand;
    }

    public void setKinoCommand(KinoCommand kinoCommand) {
        this.kinoCommand = kinoCommand;
    }

    public Map<String, Object> getExtParam() {
        return extParam;
    }

    public void setExtParam(Map<String, Object> extParam) {
        this.extParam = extParam;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public enum State {
        NUMBER_OF_ARGS,
        NUMBER_BYTE_OF_ARGS,
        ARGS_DATA
    }
}
